//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include <bulverde.h>
#include <pcf50606.h>
#include "battery.h"

CBattery g_Battery;
EXTERN_C int g_pcfBootStatus;
EXTERN_C void pcf50606_dump_registers();
EXTERN_C Boolean Drv_GpioInstall(uint_t Pin, void *Device, void * Handler);
void NotifyByIsr(int eType, int nCode, void *pParam);
void pcf50606_IrqEnable (void);
EXTERN_C void PCFWatchDogEnable(Boolean IsEnable);
EXTERN_C void PCFWatchDogReset();
unsigned long _GetElapsed();
void PoNotifyEvent(int evNum, void *pParameter);
int DetectChargerPresence();
extern int PowerWakeState;
int g_RemoveStamp = 0;
extern unsigned long lastsleep;

EXTERN_C ECode PowerCreateTimer(int *pTimerID,\
                                unsigned long Resolution,\
                                unsigned  Interval,\
                                TimerCallBackProc pCallbackProc,\
                                void * pvArg);
EXTERN_C ECode PowerStartTimer(int TimerID);
EXTERN_C ECode PowerStopTimer(int TimerID);
EXTERN_C ECode PowerReStartTimer(int TimerID,int Interval);
EXTERN_C ECode PowerShortTimer(int TimerID);
UInt32 BatteryGetVoltage();

/*
static int batt_capacity_level_0 = 3550;

static int batt_capacity_level_low_power = 3600;

static int batt_capacity_level_1 = 3600;

static int batt_capacity_level_2 = 3743;

static int batt_capacity_level_3 = 3818;

static int batt_capacity_level_4 = 3961;

static int batt_capacity_level_5 = 4190;
*/

int BatCap[12][3] = {
    {4099, 91, 4},//90%
    {4002, 80, 4},//80%
    {3942, 70, 4},//70%
    {3880, 60, 3},//60%
    {3821, 50, 3},//50%
    {3791, 40, 3},//40%
    {3767, 30, 2},//30%
    {3734, 20, 2},//20%
    {3680, 10, 1},//10%
    {3600, 5 , 0},//5%
    {3552, 3 , 0},//3%
    {3495, 2 , 0},//1%
};

int percent(int mv)
{
    int i;
    for (i = 0; i < 11; i++) {
        if (mv > BatCap[i+1][0]) break;
    }
    return (BatCap[i][1] << 16) + BatCap[i][2];
}

void dumpBattery()
{
    cprintf("%s Charger, Battery charge %s, status %s\n",
        DetectChargerPresence()?(g_Battery.m_IsUSBCharger?"Usb":"Wall"):"None",
             g_Battery.m_ChargeOk?"ok":"", g_Battery.m_IsActive?"Active":"Disable");

    cprintf("Battery voltage %d\n", BatteryGetVoltage());
}

void DumpPcfStatus()
{
    char regData;
    char ver, var;

    char * ccv[] = {
            "4.00 V",
            "4.02 V",
            "4.04 V",
            "4.06 V",
            "4.08 V",
            "4.10 V",
            "4.12 V",
            "4.14 V",
            "4.16 V",
            "4.18 V",
            "4.20 V",
            "4.22 V",
            "4.24 V",
            "4.26 V",
            "4.28 V",
            "5.00 V",
            ""
    };
    char *cmod[] = {
            "qualification mode",
            "pre charge mode",
            "trickle charge mode",
            "fast charge mode (CCCV)",
            "fast charge mode (no CC)",
            "fast charge mode (no CV)",
            "switch mode (no CC, no CV)",
            "idle mode",
            ""
    };
    char *curv[] = {
        "0.05 * IFAST",
        "0.10 * IFAST",
        "0.20 * IFAST",
        "0.40 * IFAST",
        ""
    };
    char *bvs[] = {
        "Vbat < Vbat,min",
        "Vbat,min< Vbat < Vchgcon",
        "Vchgcon < Vbat < Vbatov",
        "Vbat > Vbatov",
        ""
    };
    char *bts[] = {
        "Tbat < Tbat,min",
        "Tbat,min < Tbat < Tbat,max",
        "Tbat,max < Tbat",
        ""
    };
    char *cvs[] = {
        "too low if case DISCHGBAT= 0 then VCHGVIN< VCHGVIN,MIN & VCHGVIN > "
            "VBAT+100mV else VCHGVIN< VCHGVIN,MIN"
        "within limits",
        "too high (VCHGVIN>VCHGVIN,MAX)",
        ""
    };
    char *ccs[] = {
        "ICHG < currat*IFST",
        "currat*IFST < I < 2*IFST",
        "2* IFST < ICHG",
        ""
    };

    char * wdtime[] = { "no limit", "3 min", "6 min", "9 min", "12 min", "15 min",
                        "18 min", "21 min", "24 min", "27 min", "30 min", "33 min",
                        "36 min", "39 min", "42 min", "45 min", "48 min", "51 min",
                        "54 min", "57 min", "60 min", "63 min", "66 min", "69 min",
                        "72 min", "75 min", "78 min", "81min ", "84 min", "87 min",
                        "90 min", "93 min","" };

    char *ledpat[] = {  "000 ON = 50 ms, OFF for rest of repetition period",
                        "001 ON = 100 ms, OFF for rest of repetition period",
                        "010 ON = 200 ms, OFF for rest of repetition period",
                        "011 ON= 500 ms, OFF for rest of repetition period",
                        "100 ON = 50 ms, OFF = 50 ms, ON = 50 ms, OFF for "
                            "rest of repetition period",
                        "101 ON = 100 ms, OFF = 100 ms, ON = 100 ms, OFF for"
                            " rest of repetition period",
                        "110 ON = 200 ms, OFF = 200 ms, ON = 200 ms, OFF for"
                            " rest of repetition period",
                        "111 LED is continuously ON",
                        "" };

    char *ledped[] = {  "000 0.4 s",
                        "001 1.0 s",
                        "010 1.2 s",
                        "011 2.0 s",
                        "100 2.6 s",
                        "101 4.0 s",
                        "110 6.0 s",
                        "111 8.0 s",
                        "" };

    char *gpoc[] = {    "000: high Z",
                        "001: LED1 output",
                        "010: LED2 output",
                        "011: PWM output",
                        "100: BATOK output",
                        "101: CLK32 output",
                        "110: CHGDET output",
                        "111: constant pulled low",
                        "" };

    DUMP_OUT( cprintf("*************************************************\n") );
    //Dump PCF Version
    regData = readPCFregister(PCF50606_ID);
    var = regData & 0x0F;
    ver = (regData >> 0x04) & 0x0F;
    DUMP_OUT( cprintf("01 : PCF silicon VERSION:%d VARIANT:%d\n", ver, var) );

    //Dump PCF50606_OOCS status
    regData = readPCFregister(PCF50606_OOCS);
    DUMP_OUT( cprintf("02 : ONKEY pin status %d\n",  regData & 0x01) );
    DUMP_OUT( cprintf("03 : EXTON pin status %d\n", (regData & 0x02) >> 0x01) );
    DUMP_OUT( cprintf("04 : Reset activation during power failure situations : %s\n",\
            (regData & 0x04) \
            ? "\n     RSTN is asserted in supply voltage failure situations" \
            : "\n     RSTN is not asserted in supply voltage failure situations") );
    DUMP_OUT( cprintf("05 : Presence of main battery : %s\n",
            (regData & 0x08) \
             ? "VVBAT > VLOWBAT" \
             : "VVBAT < VLOWBAT") );
    DUMP_OUT( cprintf("06 : Presence of backup battery : %s\n",
            (regData & 0x10) \
             ? "VVBACK > VLOWBACK" \
             : "VVBACK < VLOWBACK") );
    DUMP_OUT( cprintf("07 : Presence of charger : %s\n",
            (regData & 0x20) \
             ? "VCHGVIN > VLOWCHG" \
             : "VCHGVIN < VLOWCHG") );
    DUMP_OUT( cprintf("08 : Status of IC temperature : %s\n",
            (regData & 0x40) \
             ? "TIC < TTH(THS)" \
             : "TIC > TTH(THS)") );
    DUMP_OUT( cprintf("09 : Status of the watchdog timer : %s\n",
            (regData & 0x80) \
             ? "watchdog active" \
             : "watchdog stopped") );
    //Dump PCF50606_MBCC1 status
    regData = readPCFregister(PCF50606_MBCC1);
    DUMP_OUT( cprintf("10 : Enable Charging : %s\n", (regData & 0x01)\
        ?"\n     Charging process is enabled and will start when a charger is connected."
        :"\n     Charging process is stopped and the MBC module is disabled") );
    DUMP_OUT( cprintf("11 : Fast charge start mode : %s\n", (regData & 0x02) \
            ?"auto fast mode enabled" \
            :"auto fast mode disabled") );
    DUMP_OUT( cprintf("12 : Charge mode : %s\n", cmod[(regData >> 0x02) & 0x07]) );
    DUMP_OUT( cprintf("13 : Charger detection mode : %s\n", (regData & 0x20) \
        ?"\n     charger detected if VCHGVIN>VLOWCHG"
        :"\n     charger detected if VCHGVIN>VLOWCHG & VCHGVIN>VVBAT+25mV") );
    //Dump PCF50606_MBCC3 status
    regData = readPCFregister(PCF50606_MBCC3);
    DUMP_OUT( cprintf("14 : Charge Control voltage : %s\n", ccv[regData & 0x0F]) );
    DUMP_OUT( cprintf("15 : Current setting qualification, pre and trickle charge mode : %s\n",\
        curv[(regData >> 0x04) & 0x03]) );
    //Dump PCF50606_MBCS1 Status
    regData = readPCFregister(PCF50606_MBCS1);
    DUMP_OUT( cprintf("17 : Battery voltage status : %s\n",
                    bvs[regData & 0x03]) );
    DUMP_OUT( cprintf("18 : Battery temperature status : %s\n",
                    bts[(regData >> 0x02) & 0x03]) );
    DUMP_OUT( cprintf("19 : Charger voltage status : %s\n",
                    cvs[(regData >> 0x04) & 0x03]) );
    DUMP_OUT( cprintf("20 : Charging current status : %s\n",
                    ccs[(regData >> 0x06) & 0x03]) );
     //Dump PCF50606_MBCC2 status
    regData = readPCFregister(PCF50606_MBCC2);
    DUMP_OUT( cprintf("21 : Charge Watch Dog Timer Expired Time : %s\n",
                    wdtime[regData & 0x1F]) );

    regData = readPCFregister(PCF50606_LEDC1);
    DUMP_OUT( cprintf("22 : LED1 Period  : %s LEDC1 Value %x\n",
                    ledped[regData & 0x07], regData ) );
    DUMP_OUT( cprintf("23 : LED1 Pattern : %s\n",
                    ledpat[(regData >> 3) & 0x07]) );

    regData = readPCFregister(PCF50606_LEDC2);
    DUMP_OUT( cprintf("24 : LED2 Period  : %s LEDC2 Value %x\n",
                    ledped[regData & 0x07], regData ) );
    DUMP_OUT( cprintf("25 : LED2 Pattern : %s\n",
                    ledpat[(regData >> 3) & 0x07]) );

    regData = readPCFregister(PCF50606_GPOC3);
    DUMP_OUT( cprintf("24 : GPOOD1ACT : %s GPOC3 Value %x\n",
                    gpoc[regData & 0x07], regData ) );
    DUMP_OUT( cprintf("25 : GPOOD1STB : %s\n",
                    gpoc[(regData >> 3) & 0x07]) );
    DUMP_OUT( cprintf("Current Battery Voltage Is : %dmv\n",
                    BatteryGetVoltage()) );
    DUMP_OUT( cprintf("Low Battery Re-Charge Limited Voltage"
                    " : %dmv\n", g_Battery.m_reChargeVol) );
    DUMP_OUT( cprintf("*************************************************\n") );
}

void DumpPcfIntStatus()
{
    int i;
    char regData;
    char *ir1[] = {
        "ONKEY rising edge detected (with selectable debounce function [2])",
        "ONKEY falling edge detected(with selectable debounce function [2])",
        "ONKEY at least 1 second low",
        "EXTON rising edge detected",
        "EXTON falling edge detected",
        "RESERVED",
        "RTC periodic second interrupt",
        "RTC alarm time is reached",
        ""
    };

    char *ir2[] = {
        "Charger with valid voltage has been inserted",
        "Charger has been disconnected",
        "Battery ready for fast charge: voltage and temperature within fast"
                        " charging limits",
        "Error occurred in charge mode, Charging sequence restarted",
        "Fast charge phase completed",
        "Charging protection interrupt",
        "Charger watchdog will expire in 10 seconds.",
        "Charger watchdog expires",
        ""
    };

    char *ir3[] = {
        "ADC finished the conversion, ADC data can be read",
        "An accessory is inserted",
        "An accessory is removed",
        "The touch screen is touched when touch screen module is in interrupt mode",
        "RESERVED",
        "RESERVED",
        "BVM has detected a low battery voltage (with selectable debounce"
                        " filter of 62 ms [2])",
        "THS has detected a high temperature condition (debounced with 62 ms)",
        ""
    };

    char *im1[] = {
        "Mask the ONKEYR interrupt",
        "Mask the ONKEYF interrupt",
        "Mask the ONKEY1S interrupt",
        "Mask EXTON rising edge interrupt",
        "Mask EXTON failing edge interrupt",
        "RESERVED",
        "Mask the SECOND interrupt",
        "Mask the ALARM interrupt",
        ""
    };

    char *im2[] = {
        "Mask the charger insertion interrupt",
        "Mask the charger removal interrupt",
        "Mask the charge ok interrupt",
        "Mask the fast charge error interrupt",
        "Mask the fast charge ready",
        "Mask the charging protection interrupt",
        "Mask the charger watchdog pre-alert interrupt",
        "Mask the charger watchdog expiration interrupt",
        ""
    };

    char *im3[] = {
        "Mask the ADC ready interrupt",
        "Mask the accessory insertion interrupt",
        "Mask the accessory removal interrupt",
        "Mask the touch screen press interrupt",
        "RESERVED",
        "RESERVED",
        "Mask the LOWBAT interrupt",
        "Mask the HIGHTMP interrupt",
        ""
    };

    //Dump Int1 Register
    DUMP_OUT( cprintf("\n================================================="
                    "===========\n") );
    DUMP_OUT( cprintf("Dump int register 1:\n") );
    regData = readPCFregister(PCF50606_INT1);
    for (i = 0; i < 8; i++) {
        DUMP_OUT( cprintf("BIT%d [%1d] -- %s\n", i, (regData>>i)&0x01, ir1[i]) );
    }

    DUMP_OUT( cprintf("------------------------------------------------------"
                    "------\n") );
    DUMP_OUT( cprintf("Dump int register 2:\n") );
    regData = readPCFregister(PCF50606_INT2);
    for (i = 0; i < 8; i++) {
        DUMP_OUT( cprintf("BIT%d [%1d] -- %s\n", i, (regData>>i)&0x01, ir2[i]) );
    }

    DUMP_OUT( cprintf("------------------------------------------------------"
                    "------\n"));
    DUMP_OUT( cprintf("Dump int register 3:\n") );
    regData = readPCFregister(PCF50606_INT3);
    for (i = 0; i < 8; i++) {
        DUMP_OUT( cprintf("BIT%d [%1d] -- %s\n", i, (regData>>i)&0x01, ir3[i]) );
    }
    DUMP_OUT( cprintf("------------------------------------------------------"
                    "------\n"));
    DUMP_OUT( cprintf("Dump int mask register 1: (Enable : 0 Mask : 1)\n") );
    regData = readPCFregister(PCF50606_INT1M);
    for (i = 0; i < 8; i++) {
        DUMP_OUT( cprintf("BIT%d [%1d] -- %s\n", i, (regData>>i)&0x01, im1[i]) );
    }
    DUMP_OUT( cprintf("-------------------------------------------------------"
                    "-----\n") );
    DUMP_OUT( cprintf("Dump int mask register 2: (Enable : 0 Mask : 1)\n") );
    regData = readPCFregister(PCF50606_INT2M);
    for (i = 0; i < 8; i++) {
        DUMP_OUT( cprintf("BIT%d [%1d] -- %s\n", i, (regData>>i)&0x01, im2[i]) );
    }
    DUMP_OUT( cprintf("-------------------------------------------------------"
                    "-----\n") );
    DUMP_OUT( cprintf("Dump int mask register 3: (Enable : 0 Mask : 1)\n") );
    regData = readPCFregister(PCF50606_INT3M);
    for (i = 0; i < 8; i++) {
        DUMP_OUT( cprintf("BIT%d [%1d] -- %s\n", i, (regData>>i)&0x01, im3[i]) );
    }
    DUMP_OUT( cprintf("\n====================================================="
                    "=======\n") );
}

EXTERN_C void LED_ResetFromWakeUP()
{
//    cprintf("LED_ResetFromWakeUP ..... A\n");
//        cprintf("LED_ResetFromWakeUP ..... B\n");
    g_Battery.LED_ChargeEnable(FALSE);
    g_Battery.LED_NormalEnable(TRUE);
    g_Battery.LED_NormalStyleSetting(g_Battery.m_lednorPED, g_Battery.m_lednorPAT);
}

EXTERN_C void LED_SetupForSleep()
{
//    cprintf("LED_SetupForSleep ..... A\n");
//        cprintf("LED_SetupForSleep ..... B\n");
    g_Battery.LED_ChargeEnable(FALSE);
    g_Battery.LED_NormalEnable(TRUE);
    g_Battery.LED_NormalStyleSetting(LEDPED_80S, ON_050MS);
}

void Charger_IrqEnable(void)
{
    //for gpio107
// GPDR3 &= ~(1 << 11);
// GAFR3_L &= ~((1 << 22) | (1 << 23)); //gpio107 is general gpio
// GFER3 |= (1 << 11);
// GRER3 |= (1 << 11);
//    DzDelay(100);

    //for gpio41
    GPDR1 &= ~(1 << (9));
    GAFR1_L &= ~((1 << 19) | (1 << 18));
    GFER1 |= (1 << 9);
    GRER1 |= (1 << 9);

    //config pcf
    //writePCFregister(PCF50606_MBCC1,readPCFregister(PCF50606_MBCC1)&0xfe);
}

//
// Some encapsulated functions that control battery related
// registers on PCF50606
//

//
//  Description:    Get the current battery voltage. The unit is mV
//                  because kernel can not use float point
//
UInt32 BatteryGetVoltage()
{
    //
    // note:the real voltage is caculated from the relation:
    // real voltage = volt * 2.4 / 256 + 3.0 (we use 8 bit mode here)
    // See PCF56060 spec page 73 for detailed information
    //

    int time_out = 100;
    UInt32 volt = readPCFregister(PCF50606_ADCC2);
    volt = 0x82;
    writePCFregister(PCF50606_ADCC2,volt);  // BATVOLT substractor, 8 bit mode
    DzDelay(100);
    volt = 0x83;
    writePCFregister(PCF50606_ADCC2,volt);
    int i=0;

    // wait for the conversion complele. ADCS2 BIT 7 is 1 means converstion complete
    while((i++ < time_out)&&!(readPCFregister(PCF50606_ADCS2)&0x80));

    // read the 8 most significant bits
    volt = readPCFregister(PCF50606_ADCS1);

    // calculate the real voltage in mV
    volt = volt * 2400 / 256 + 3000;

    return volt;
}

/*
enum BatteryChargeMode
{
    BCM_Qualification   = 0,
    BCM_Precharge       = 1,
    BCM_Ticklecharge    = 2,
    BCM_FastChargeCCCV  = 3,
    BCM_FastChargeCV    = 4,
    BCM_FastChargeCC    = 5,
    BCM_SwitchMode      = 6,
    BCM_Idle            = 7
};
*/
//
//  Description: Get current charge mode
//              The charge mode can be retrieved from bit 2-4 of MBCC1
//              register on PCF50606. There are 8 possibilties,
//              See page 67 of PCF50606 Spec for detailed information
//
int BatteryGetCharegeMode()
{
    // MBCC1 register BIT 2-4
    return (readPCFregister(PCF50606_MBCC1) >> 2) & 0x7;
}

//
//  Description:    Set the voltage threshold, if the battery voltage is lower
//                  than the input value, an interrupt will be issued.
//                  The interrupt is handled by OnLowBattery() function.
//                  See page 88 of PCF50606 spec
//
//  Input:          nThreshold can be [0..8], the meaning is as follows:
//                  0   :   No low battery interrupt
//                  1   :   2.80 V
//                  2   :   2.90 V
//                  3   :   3.00 V
//                  4   :   3.10 V
//                  5   :   3.20 V
//                  6   :   3.30 V
//                  7   :   3.40 V
//
void BatterySetThresholdVoltage(char btv)
{
    char regData = readPCFregister(PCF50606_BVMC);
    regData &= (~0x0E);
    regData |= (btv << 1);
    writePCFregister(PCF50606_BVMC, regData);
}

void BatteryChargeVoltageSetting(char vol)
{
    char regData = readPCFregister(PCF50606_MBCC3);
    regData &= (~0x0F);
    regData |= vol;
    writePCFregister(PCF50606_MBCC3, regData);
}

void BatteryChargeEnable(Boolean IsEnable)
{
    char regData = readPCFregister(PCF50606_MBCC1);
    if (TRUE == IsEnable) {
        regData |= 0x01;
    }
    else {
        regData &= (~0x01);
    }
    writePCFregister(PCF50606_MBCC1, regData);
}

void BatteryAUTOFSTEnable(Boolean IsEnable)
{
    char regData = readPCFregister(PCF50606_MBCC1);
    if (TRUE == IsEnable) {
        regData |= 0x02;
    }
    else {
        regData &= (~0x02);
    }
    writePCFregister(PCF50606_MBCC1, regData);
}

void BatteryChargeModeSetting(char mode)
{
    char regData = readPCFregister(PCF50606_MBCC1);
    regData &= (~0x1D);
    regData |= (mode << 2);
    writePCFregister(PCF50606_MBCC1, regData);
}

void ChargeWatchDogReset(Boolean IsReset)
{
    char regData = readPCFregister(PCF50606_MBCC1);
    if (TRUE == IsReset) {
        regData |= 0x40;
    }
    else {
        regData &= (~0x40);
    }
    writePCFregister(PCF50606_MBCC1, regData);
}

void ChargeWatchDogSetting(char regData)
{
    writePCFregister( PCF50606_MBCC2, regData & 0x1F );
}

/*********************************
    GPIO39  USB_CON_DET     (D-)
    GPIO40  USB_FULLSPEED   (D+)
    GPIO41  USB_OTG_ID      (ID)
    GPIO107 USB_DEC_N       (VBUS)
 **********************************/

int IsWallCharger()
{
    unsigned long bWallCharger = 0;

    unsigned long gpdr1, gplr1;
    gpdr1 = GPDR1;gplr1 = GPLR1;

    //dump gpio41, 40, 39
    BAT_DBG( cprintf("******ID %x, (%x)D+ %x, (%x)D- %x\n", GPLR1 >> 9 & 1,
        GPDR1 >> 8 & 1, GPLR1 >> 8 & 1,
        GPDR1 >> 7 & 1, GPLR1 >> 7 & 1));

    GPDR1 |= (1<<7);//39 out
    GPCR1 |= (1<<7);//39 out low
    GPDR1 |= (1<<8);//40 out
    GPSR1 |= (1<<8);//40 out high
    GPDR1 &= ~(1<<7);//39 in
    DzDelay(1);
    BAT_DBG( cprintf("ID %x, D+ %x >>>> D- %x\n", GPLR1 >> 9 & 1, GPLR1 >> 8 & 1, GPLR1 >> 7 & 1));
    bWallCharger = (GPLR1 >> 7 & 1);
    GPCR1 |= (1<<8);//40 out low

    GPDR1 |= (1<<7);//39 out
    GPSR1 |= (1<<7);//39 out high
    GPDR1 &= ~(1<<8);//40 in
    DzDelay(1);
    BAT_DBG( cprintf("ID %x, D+ %x <<<< D- %x\n", GPLR1 >> 9 & 1, GPLR1 >> 8 & 1, GPLR1 >> 7 & 1));
    bWallCharger = bWallCharger && (GPLR1 >> 8 & 1);
    GPCR1 |= (1<<7);//39 out low

    GPDR1 = gpdr1;
    (gpdr1 >> 8 & 1 && gplr1 >> 8 & 1)?GPSR1 |= (1<<8):GPCR1 |= (1<<8);
    (gpdr1 >> 7 & 1 && gplr1 >> 7 & 1)?GPSR1 |= (1<<7):GPCR1 |= (1<<7);

    //dump gpio41, 40, 39
    BAT_DBG( cprintf("******ID %x, (%x)D+ %x, (%x)D- %x\n", GPLR1 >> 9 & 1,
        GPDR1 >> 8 & 1, GPLR1 >> 8 & 1,
        GPDR1 >> 7 & 1, GPLR1 >> 7 & 1));

    BAT_DBG( cprintf("bWallCharger %x\n", bWallCharger));
    return bWallCharger;
}

//
//  This is a helper function to detect wether the current charger
//  is wall charger or USB charger.
//  return      1 indicates USB Charger
//              0 indicates Wall Charger
//
int IsUSBCharger()
{
    BAT_DBG( cprintf("USB DETECT %d , OTG_ID %d~~~~\n", GPLR3 >> 11 & 1, GPLR1 >> 9 & 1));
    Boolean bDetect = !(GPLR3 >> 11 & 1);//0: Detect
    if (!bDetect) return 0;
    
    int nType;
    nType = (GPLR1 >> 9) & 0x01;
    if (nType == 1) {//maybe D+=D-
        nType = !IsWallCharger();
    }
    return nType;
}

int DetectChargerPresence()
{
    char regData = readPCFregister(PCF50606_OOCS);
    if (regData & 0x20) {
        return 1;
    }
    return 0;
}

int DetectChargerNoPower()
{
    return g_Battery.m_IsActive && !DetectChargerPresence();
}

void AutoVoltageDetect(void * p)
{
    BAT_DBG( cprintf("Auto Voltage Dectect Timer Pulse!\n") );
    CBattery *pCBat = (CBattery*)p;
    pCBat->m_SoftDog = 1;
    (pCBat->m_ChargeEvent).NotifyByIsr();
}

//
//  This is a helper function to detect wether
//  main battery is in charge.used for power manager
//
int IsInCharge()
{
    return g_Battery.m_bInCharge;
}

void BatteryWake()
{
    EmIsrParameter Param;
    Param.nParameter1 = PM_BATTERY_WAKE;//Events
    Param.nParameter2 = 0;//Status
    Param.nParameter3 = BatteryGetVoltage();
    Param.nParameter4 = lastsleep;
    PoNotifyEvent(212, &Param);
    lastsleep = 0;//reset

    PowerShortTimer(g_Battery.m_VolDetectTM);

    return ;
}

ECode CBattery::Read(
    /* [in] */ Int64 u64Offset,
    /* [in] */ Int32 bytesToRead,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode  CBattery::Write(
    /* [in] */ Int64 u64Offset,
    /* [in] */ const MemoryBuf &buffer,
    /* [out] */ Int32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CBattery::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ const MemoryBuf &inBuffer,
    /* [out] */ MemoryBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    ECode ec = NOERROR;
    char *pInData = (char*)inBuffer.GetPayload();

    switch (nControlCode) {
        case 1:{
            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            if (pOutBuffer->GetCapacity() < 8) return E_ACCESS_DENIED;
        // Get the current battery voltage, in millivolts (mV).
            UInt32 CurVoltage = BatteryGetVoltage();
            UInt32 *pParam = (UInt32 *)pOutBuffer->GetPayload();
            pParam[0] = CurVoltage;
            pParam[1] = percent(CurVoltage);
            pOutBuffer->SetUsed(8);
            break;
        }
        case 2:{
        // Get current charge status
            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            *((Boolean *)pOutBuffer->GetPayload()) = m_bInCharge;
            pOutBuffer->SetUsed(sizeof(Boolean));

            break;
        }

        case 3:{
        // Get the current charge mode
            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            *(int *)pOutBuffer->GetPayload() = BatteryGetCharegeMode();
            pOutBuffer->SetUsed(sizeof(int));

            break;
        }
        case 4:{
        // Get the charger type -1: None, 0: wall charger 1: USB charger
            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            if (!DetectChargerPresence())
                *(int *)pOutBuffer->GetPayload() = -1;
            else
                *((int *)pOutBuffer->GetPayload()) = IsUSBCharger();
            BAT_DBG( cprintf("usb charger return %d.\n", IsUSBCharger()) );
            pOutBuffer->SetUsed(sizeof(int));

            break;
        }

        case 10:{
        // set threshold voltage. After battery reach the voltage
        // interrupt will be issued.
            int volt = 0;
            if (inBuffer.GetPayload() == NULL)
                volt = 0; // default not threshold
            else
                volt = *(int *)inBuffer.GetPayload();

            BatterySetThresholdVoltage((char)volt);

            break;
        }
        case 12:{
            m_ChargeOk = *(int *)inBuffer.GetPayload();
            m_ledchgPED = LEDPED_80S;
            break;
        }
        case PCF_DUMP_STATUS:
            DumpPcfStatus();
            break;

        case PCF_BATCHARGE_ENABLE:
            BatteryChargeEnable(TRUE);
            break;

        case PCF_BATCHARGE_DISABLE:
            BatteryChargeEnable(FALSE);
            break;

        case PCF_SET_RECHARGE_VOL://Change the re-charge voltage (3.7 ~ 4.1)
            if (inBuffer.GetPayload() != NULL) {
                BatteryReChargeVoltageSetting(*(UInt32*)inBuffer.GetPayload());
            }
            break;

        case PCF_SET_INFOPOST_INTERVAL://Change the battery info post interval
            if (inBuffer.GetPayload() != NULL) {
                BatterySetInfoPostInterval(*(UInt32*)inBuffer.GetPayload());
                EnableAutoVoltageDetect(TRUE);
            }
            break;

        case PCF_CHANGE_LED_SETTING://Led Charge Setting
            if (pInData != NULL && inBuffer.GetUsed() >= 2) {
                ChangeChargeLedSetting(pInData[0], pInData[1]);
                if (TRUE == m_IsActive) {
                    LED_ChargeStyleSetting(m_ledchgPED, m_ledchgPAT);
                }
            }
            break;

        case PCF_NORMAL_LED_SETTING://Led Normal Setting
            pInData = (char *)inBuffer.GetPayload();
            if (pInData != NULL && inBuffer.GetUsed() >= 2) {
                ChangeNormalLedSetting(pInData[0], pInData[1]);
                if (FALSE == m_IsActive) {
                    LED_NormalStyleSetting(m_lednorPED, m_lednorPAT);
                }
            }
            break;

        case PCF_CHARGE_LED_ON:
            LED_ChargeEnable(TRUE);
            break;

        case PCF_CHARGE_LED_OFF:
            LED_ChargeEnable(FALSE);
            break;

        case PCF_NORMAL_LED_ON:
            LED_NormalEnable(TRUE);
            break;

        case PCF_NORMAL_LED_OFF:
            LED_NormalEnable(FALSE);
            break;
        case PCF_WATCHDOG_RESET:
        //PCFWatchDogEnable(FALSE);
            PCFWatchDogReset();
            break;

//    case 7686:
//        if ((Byte *)inBuffer != NULL) {
//            pInData = (Byte *)inBuffer;
//            writePCFregister(PCF50606_GPOC3, pInData[0]);
//            cprintf("PCF50606_GPOC3 %08x\n", readPCFregister(PCF50606_GPOC3));
//        }
//        break;
//
//    case 7687:
//        if ((char *)inBuffer != NULL) {
//            pInData = (char*)inBuffer;
//            writePCFregister(PCF50606_GPOC1, pInData[0]);
//            cprintf("PCF50606_GPOC1 %08x\n", readPCFregister(PCF50606_GPOC1));
//        }
//        break;
//
//    case 7688:
//        if ((char *)inBuffer != NULL) {
//            pInData = (char*)inBuffer;
//            writePCFregister(PCF50606_GPOC4, pInData[0]);
//            cprintf("PCF50606_GPOC4 %08x\n", readPCFregister(PCF50606_GPOC4));
//        }
//        break;
//
//    case 7689:
//        if ((char *)inBuffer != NULL) {
//            pInData = (char*)inBuffer;
//            writePCFregister(PCF50606_GPOC5, pInData[0]);
//            cprintf("PCF50606_GPOC5 %08x\n", readPCFregister(PCF50606_GPOC5));
//        }
//        break;
//
//    case 7690:
//        if ((char *)inBuffer != NULL) {
//            pInData = (char*)inBuffer;
//            writePCFregister(PCF50606_GPOC2, pInData[0]);
//            cprintf("PCF50606_GPOC2 %08x\n", readPCFregister(PCF50606_GPOC2));
//        }
//        break;

        default:
            ec = E_NOT_IMPLEMENTED;
            break;
    }

    return ec;
}

void CBattery::LED_ChargeEnable(Boolean IsEnable)
{
    if (TRUE == IsEnable) {
        writePCFregister(PCF50606_GPOC3, 0x09);
    }
    else {
        writePCFregister(PCF50606_GPOC3, 0x00);
    }
}

void CBattery::LED_NormalEnable(Boolean IsEnable)
{
    if (TRUE == IsEnable) {
        writePCFregister(PCF50606_GPOC1, 0x0A);
    }
    else {
        writePCFregister(PCF50606_GPOC1, 0x07);
    }
}

void CBattery::LED_ChargeStyleSetting(char period, char pattern)
{
    writePCFregister(PCF50606_LEDC1, LED_ONACTIVE(1) | LED_ONCHARGE(1) \
                 | LED_PERIOD(period) | LED_PATTERN(pattern));
}

void CBattery::LED_NormalStyleSetting(char period, char pattern)
{
    writePCFregister(PCF50606_LEDC2, LED_ONACTIVE(1) | LED_ONCHARGE(1) \
                 | LED_PERIOD(period) | LED_PATTERN(pattern));
}

void CBattery::ChangeChargeLedSetting(char period, char pattern)
{
    m_ledchgPAT = pattern;
    m_ledchgPED = period;
}

void CBattery::ChangeNormalLedSetting(char period, char pattern)
{
    m_lednorPAT = pattern;
    m_lednorPED = period;
}

void CBattery::BatteryReChargeVoltageSetting(UInt32 vol)
{
    m_reChargeVol = vol;
}

void CBattery::BatterySetInfoPostInterval(UInt32 interval)
{
    m_InfoPostInterval = interval;
}

void CBattery::EnableAutoVoltageDetect(Boolean IsEnable)
{
    if (TRUE == IsEnable) {
        if (-1 == m_VolDetectTM) {
            PowerCreateTimer(&m_VolDetectTM, 1, BAT_CAP_INTERVAL, AutoVoltageDetect, (void *)this);
        }
        PowerStartTimer(m_VolDetectTM);
        BAT_DBG( cprintf("Get Timer ID %d\n", m_VolDetectTM) );
    }
    else {
        PowerStopTimer(m_VolDetectTM);
        BAT_DBG( cprintf("Stop Timer ID %d\n", m_VolDetectTM) );
        m_VolDetectTM = -1;
    }
}

ECode CBattery::ChargeMgmThread()
{
    WaitResult wr;
    EmIsrParameter Param;
    UInt32 CurVoltage;
    Boolean IsLowBat = FALSE, IsFastRdy = FALSE;
    m_BatteryIntStat = (g_pcfBootStatus >> 16);
    m_ChargeIntStat = (g_pcfBootStatus >> 8);
    m_ChargeIntStat &= ~(CHGINS_INT);
    BatteryAUTOFSTEnable(TRUE);
    LED_ChargeEnable(FALSE);
    LED_NormalEnable(TRUE);
    LED_NormalStyleSetting(m_lednorPED, m_lednorPAT);
    BatteryChargeEnable(TRUE);
    EnableAutoVoltageDetect(TRUE);

    do {
        while (0 != PowerWakeState) {
            cputs("@");
            DzSleep(30, NULL);
        }

        Param.nParameter1 = 0;//Events
        Param.nParameter2 = 0;//Status
        Param.nParameter3 = 0;//Battery Voltage
        Param.nParameter4 = 0;//Battery Capacity & Level

        BAT_DBG( cprintf("Charge Interrupt Register Value %02x\n",m_ChargeIntStat) );
        BAT_DBG( cprintf("Battery Interrupt Register Value %02x\n", m_BatteryIntStat) );

        if (m_BatteryIntStat & HIGHTMP_INT) {
            BAT_DBG( cprintf("High Battert Temperture Captured!\n") );
            Param.nParameter2 |= PM_BAT_HIGHTMP;
        }

        if (m_ChargeIntStat & CHGRM_INT || DetectChargerNoPower()) {
            BAT_DBG( cprintf("Charger Remove Interrupt Captured!\n") );
            g_Battery.m_bInCharge = FALSE;
            g_RemoveStamp = _GetElapsed();
            m_IsActive = FALSE;
            Param.nParameter1 |= PM_CHARGE_REMOVE;// | PM_BATTERY_CAP;
            if (1 == m_IsUSBCharger) {
                Param.nParameter2 |= PM_CHARGER_USB;
            }
            else {
                Param.nParameter2 |= PM_CHARGER_WALL;
            }
            Param.nParameter2 |= PM_CHARGE_STOP;
            Param.nParameter3 = BatteryGetVoltage();
            BAT_DBG( cprintf("Charge Processing is Stop!\n") );
            LED_ChargeEnable(FALSE);
            LED_NormalEnable(TRUE);
            LED_NormalStyleSetting(m_lednorPED, m_lednorPAT);
            PowerReStartTimer(g_Battery.m_VolDetectTM, BAT_CAP_INTERVAL);
            NotifyByIsr(EVENT_INPUT, 0, NULL);
        }

        if (m_ChargeIntStat & CHGINS_INT) {
            BAT_DBG( cprintf("Charger Insert Interrupt Captured!\n") );
            DzSleep(50, NULL);//Wait for USB_OTG_ID pin status ready
            if (1 == DetectChargerPresence()) {
                BAT_DBG( cprintf("The Charger is Present\n") );
                m_IsUSBCharger = IsUSBCharger();
                g_Battery.m_ChargeOk = FALSE;
                g_Battery.m_bInCharge = TRUE;
                IsFastRdy = TRUE;
                m_ledchgPAT = ON_INFMS;
                m_ledchgPED = LEDPED_40S;
                Param.nParameter1 |= PM_CHARGE_INSERT;// | PM_BATTERY_CAP;
                Param.nParameter1 &= ~PM_CHARGE_REMOVE;
                Param.nParameter3 = BatteryGetVoltage();
                PowerReStartTimer(g_Battery.m_VolDetectTM, BAT_CAP_INTERVAL);
                NotifyByIsr(EVENT_INPUT, 0, NULL);
            }
        }

        if (m_BatteryIntStat & LOWBAT_INT) {
            BAT_DBG( cprintf("Low Battert Voltage Interrupt Captured!\n") );
            // Add event
            if (_GetElapsed() - g_RemoveStamp > 2) {
                Param.nParameter3 = BatteryGetVoltage();
                Param.nParameter2 |= PM_BAT_LOWVOL;
                Param.nParameter1 |= PM_BATTERY_CAP;
            }
            else {
                BAT_DBG( cprintf("Maybe verylow voltage while charger remove.\n") );
                PCFWatchDogReset();
            }
            IsLowBat = TRUE;
        }

        if (m_ChargeIntStat & CHGERR_INT) {
            BAT_DBG( cprintf("Charge Error Interrupt Captured!\n") );
            Param.nParameter1 |= PM_CHARGE_ERR;
        }

        if (m_ChargeIntStat & CHGFRDY_INT) {
            BAT_DBG( cprintf("Fast charge phase completed Interrupt Captured!\n") );
            IsLowBat = FALSE;
            IsFastRdy = FALSE;
            m_IsActive = FALSE;
            m_ledchgPAT = ON_200MS;
            Param.nParameter1 |= PM_FASTCHG_FIN;
            if (!m_ChargeOk) {
                Param.nParameter2 |= PM_CHARGE_STOP;
                BAT_DBG( cprintf("Charge Processing is Stop!\n") );
            }
            else {
                BAT_DBG( cprintf("Charge logic is Ok!\n") );
            }

            LED_ChargeEnable(FALSE);
            LED_NormalEnable(TRUE);
            LED_NormalStyleSetting(m_lednorPED, m_lednorPAT);
        }

        if (m_ChargeIntStat & CHGFOK_INT) {
            IsFastRdy = TRUE;
            BAT_DBG( cprintf("Fast Charge Ready Interrupt Captured!\n") );
            Param.nParameter1 &= (~PM_FASTCHG_FIN);
            Param.nParameter1 |= PM_FASTCHG_RDY;
        }

        if (m_SoftDog == 0x01) {
            BAT_DBG( cprintf("Soft Charge WatchDog Pulse!\n") );
            m_SoftDog = 0;
            CurVoltage = BatteryGetVoltage();
            BAT_DBG( cprintf("Current Battery Voltage Is : %dmv Low Battery"
                            " Re-Charge Voltage : %dmv\n", CurVoltage,
                            m_reChargeVol) );
            if ((m_reChargeVol > CurVoltage)
                    && (FALSE == m_IsActive)) {
                BAT_DBG( cprintf("The Charge APE Bit Is Disable, Set The Flag"
                                " to Re-Active The Charge APE!\n") );
                IsFastRdy = TRUE;
            }
            Param.nParameter1 |= PM_BATTERY_CAP;
            Param.nParameter3 = CurVoltage;
            Param.nParameter4 = percent(CurVoltage);
            BAT_DBG( cprintf("Battery Capacity is %02d%% Battery Level %d!\n", \
                            (Param.nParameter4 & 0xFFFF0000) >> 16,
                            Param.nParameter4 & 0x0000FFFF));
            BAT_DBG( cprintf("Soft Charge WatchDog Reset!\n") );
        }

        if (m_ChargeIntStat & CHGPROT_INT) {
            BAT_DBG( cprintf("Charging protection Interrupt Captured!\n") );
            Param.nParameter1 |= PM_CHARGE_POT;
        }

        if (m_ChargeIntStat & CHGWD10S_INT) {
            ChargeWatchDogReset(TRUE);
            BAT_DBG( cprintf("Charger watchdog will expire in 10 seconds"
                            " Interrupt Captured!\n") );
            Param.nParameter1 |= PM_CHGWD10S_DEC;
        }

        if (m_ChargeIntStat & CHGWDEXP_INT) {
            BAT_DBG( cprintf("Charger watchdog expires Interrupt Captured!\n") );
            Param.nParameter1 |= PM_CHGWD_EXP;
        }

//Charge Module Action
        if (TRUE == g_Battery.m_bInCharge) {
            if (1 == m_IsUSBCharger) {
                BAT_DBG( cprintf("USB Charger Insert!\n") );
                Param.nParameter2 |= PM_CHARGER_USB;
            }
            else {
                BAT_DBG( cprintf("Wall Charger Insert!\n") );
                Param.nParameter2 |= PM_CHARGER_WALL;
            }
            if ((TRUE == IsFastRdy
                    || TRUE == IsLowBat)
                    && (FALSE == m_IsActive)) {
                BAT_DBG( cprintf("Battery Charge APE Is Enable!\n") );
                BatteryChargeEnable(TRUE);
                LED_ChargeEnable(TRUE);
                LED_NormalEnable(FALSE);
                LED_ChargeStyleSetting(m_ledchgPED, m_ledchgPAT);
                m_IsActive = TRUE;
                if (!m_ChargeOk) {
                    Param.nParameter2 |= PM_CHARGE_START;
                    BAT_DBG( cprintf("Charge Processing is Active!\n") );
                }
                else {
                    BAT_DBG( cprintf("Charge logic is Ok!\n") );
                }
                if (TRUE == IsLowBat) {
//                    cprintf("Battery Low Charge WatchdogReset!\n");
                    PCFWatchDogEnable(FALSE);
                    PCFWatchDogReset();
                }
            }
        }

        PoNotifyEvent(212, &Param);

        m_ChargeIntStat = 0;
        m_BatteryIntStat = 0;

//        DumpPcfStatus();

        m_ChargeEvent.Wait(&wr, NULL);
        BAT_DBG( cprintf("Charge Monitor Thread Detect Charge "
                        "Status Changed!\n") );
    } while (1);

    return NOERROR;
}

#define PARA_ADC_OFFSET          0x200
#define PARA_ADC_LENGTH          16
EXTERN_C int ReadTatTable(unsigned char * ptr, unsigned int size);

void CBattery::Calibrate()
{
    Byte  TattBuffer[PARA_ADC_OFFSET + PARA_ADC_LENGTH];
    memset(TattBuffer, 0, PARA_ADC_OFFSET + PARA_ADC_LENGTH);

    if (!ReadTatTable(TattBuffer, PARA_ADC_OFFSET + PARA_ADC_LENGTH)) {
        cprintf("Battery calibrate failed:wrong tat parameter\n");
        return;
    }

    //Read adc parameter
    memcpy(m_delta, &TattBuffer[PARA_ADC_OFFSET], PARA_ADC_LENGTH);
    cprintf("Battery calibrate delta: (%x, %x) (%x, %x)\n",
        m_delta[0], m_delta[1], m_delta[2], m_delta[3]);

    return ;
}

extern int g_bspIsUSBCharger;//Share global var with bsp, Update soon
void CBattery::Initialize()
{
    m_bInCharge = DetectChargerPresence();
    m_IsActive = FALSE;
    m_VolDetectTM = -1;
    m_ChargeIntStat = 0;
    m_BatteryIntStat = 0;
    m_SoftDog = 0;
    m_reChargeVol = RECHARGE_VOL_DEF;
    m_InfoPostInterval = 1;
    m_ledchgPAT = ON_INFMS;
    m_ledchgPED = LEDPED_40S;
    m_lednorPAT = OFO200MS;
    m_lednorPED = LEDPED_40S;
    m_ChargeOk = FALSE;
    m_IsUSBCharger = g_bspIsUSBCharger;
    Calibrate();
}

ECode CHG_CrThread(void *arg)
{
    g_Battery.ChargeMgmThread();
    return NOERROR;
}

ECode CreateChargeThread()
{
    ECode ec;
    ec = DzCreateKernelThread(CHG_CrThread,
        (PVoid)NULL, CreateThreadFlag_System, ThreadPriorityRank_Normal, NULL);
    if (FAILED(ec)) {
        BAT_DBG( cprintf("unable to create ctrl thread ec : %08x\n", ec) );
        return ec;
    }
    BAT_DBG( cprintf("Create Charge Management Thread Succeeded!\n") );
    return ec;
}

void GetChargeIntStat(void *intStat, Boolean *bArg)
{

    g_Battery.m_ChargeIntStat = *((char*)intStat);
    g_Battery.m_BatteryIntStat = *((char*)intStat + 1);
}

//EXTERN_C void PCF50606_Reset_Manual();

EXTERN IDeviceDriver * CDECL CreateBattery(uint_t uDeviceNo, void *pvParameter)
{
//    PCF50606_Reset_Manual();
    g_Battery.Initialize();
    CreateChargeThread();
    Drv_PcfAllChargeIrqInstall(&(g_Battery.m_ChargeEvent),
                    (void *)GetChargeIntStat);

//    Drv_PcfIrqInstall(PCF50606_CHGINSM_IRQ, NULL, (void *)ChargerInsert);
//    Drv_PcfIrqInstall(PCF50606_CHGRMM_IRQ, NULL, (void *)ChargerRemove);
//    Drv_PcfIrqInstall(PCF50606_LOWBATM_IRQ, NULL, (void *)OnLowBattery);
    BAT_DBG( cprintf("PCF CHARGE IRQ ENABLE!\n") );
//    Drv_GpioInstall(41,NULL,(void *)CablePlugIn);
//    Charger_IrqEnable();
    pcf50606_IrqEnable();
    BAT_DBG( cprintf("Battery Driver Initilized. (%x)\n", g_Battery.m_IsUSBCharger) );
    return &g_Battery;
}
